package com.my.blog.website.service.impl;

import cn.hutool.core.io.FileUtil;
import com.github.pagehelper.PageHelper;
import com.my.blog.website.constant.WebConst;
import com.my.blog.website.controller.admin.FileUploadController;
import com.my.blog.website.dao.*;
import com.my.blog.website.dto.MetaDto;
import com.my.blog.website.dto.Types;
import com.my.blog.website.exception.TipException;
import com.my.blog.website.modal.Bo.ArchiveBo;
import com.my.blog.website.modal.Bo.BackResponseBo;
import com.my.blog.website.modal.Bo.StatisticsBo;
import com.my.blog.website.modal.Vo.CommentVo;
import com.my.blog.website.modal.Vo.ContentVo;
import com.my.blog.website.modal.Vo.MetaVo;
import com.my.blog.website.modal.Vo.UploadVo;
import com.my.blog.website.service.ISiteService;
import com.my.blog.website.utils.*;
import com.my.blog.website.utils.backup.Backup;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by BlueT on 2017/3/7.
 */
@Service
public class SiteServiceImpl implements ISiteService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SiteServiceImpl.class);

    @Resource
    private CommentVoMapper commentDao;

    @Resource
    private ContentVoMapper contentDao;

    @Resource
    private AttachVoMapper attachDao;

    @Resource
    private UploadVoMapper uploadDao;

    @Resource
    private MetaVoMapper metaDao;

    @Value("${app.name}")
    private String appName;

    @Override
    public List<CommentVo> recentComments(int limit) {
        return recentComments(limit, 0);
    }

    @Override
    public List<CommentVo> recentComments(int limit,int uid) {
        LOGGER.debug("Enter recentComments method:limit={}", limit);
        if (limit < 0 || limit > 10) {
            limit = 10;
        }
        /*CommentVoExample example = new CommentVoExample();
        example.setOrderByClause("created desc");
        PageHelper.startPage(1, limit);
        List<CommentVo> byPage = commentDao.selectByExampleWithBLOBs(example);*/

        Example example = new Example(CommentVo.class);
        if (uid!=0){
            example.createCriteria().andEqualTo("ownerId",uid);
        }
        example.setOrderByClause("created desc");
        PageHelper.startPage(1, limit);
        List<CommentVo> byPage = commentDao.selectByExample(example);
        LOGGER.debug("Exit recentComments method");
        return byPage;
    }

    @Override
    public List<ContentVo> recentContents(int limit) {
        return recentContents( limit, 0) ;
    }

    @Override
    public List<ContentVo> recentContents(int limit,int uid) {
        LOGGER.debug("Enter recentContents method");
        if (limit < 0 || limit > 10) {
            limit = 10;
        }
        Example example = new Example(ContentVo.class);
        Example.Criteria criteria = example.createCriteria();
        if (uid!=0){
            criteria.andEqualTo("authorId",uid);
        }

        criteria.andEqualTo("status",Types.PUBLISH.getType()).andEqualTo("type",Types.ARTICLE.getType());
        example.setOrderByClause("modified desc");
        PageHelper.startPage(1, limit);
        List<ContentVo> list = contentDao.selectByExample(example);
        LOGGER.debug("Exit recentContents method");
        return list;
    }

    /*@Override
    public BackResponseBo backup(String bk_type, String bk_path, String fmt) throws Exception {
        BackResponseBo backResponse = new BackResponseBo();
        if ("attach".equals(bk_type)) {
            if (StringUtils.isBlank(bk_path)) {
                throw new TipException("请输入备份文件存储路径");
            }

            File bakPath = new File(bk_path);
            if (!bakPath.exists()) {
                bakPath.mkdirs();
            }

//            if (!(new File(bk_path)).isDirectory()) {
//                throw new TipException("请输入一个存在的目录");
//            }
            // 上传文件的路径
            //String bkAttachDir = AttachController.CLASSPATH + "upload";
            String bkAttachDir = Commons.getUploadBasePath();
            //String bkThemesDir = FileUploadController.CLASSPATH + "templates/themes";
            String bkThemesDir = FileUploadController.CLASSPATH + "templates/themes";

            String fname = DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".zip";

            String attachPath = bk_path + "/" + "attachs_" + fname;
            String themesPath = bk_path + "/" + "themes_" + fname;

            ZipUtils.zipFolder(bkAttachDir, attachPath);
            ZipUtils.zipFolder(bkThemesDir, themesPath);

            backResponse.setAttachPath(attachPath);
            backResponse.setThemePath(themesPath);
        }
        if ("db".equals(bk_type)) {

            //String bkAttachDir = AttachController.CLASSPATH + "upload/";
            String bkAttachDir = bk_path + File.separator + "sql_bak/";
            if (!(new File(bkAttachDir)).isDirectory()) {
                File file = new File(bkAttachDir);
                if (!file.exists()) {
                    file.mkdirs();
                }
            }
            String sqlFileName = "tale_" + DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".sql";
            String zipFile = sqlFileName.replace(".sql", ".zip");

            Backup backup = new Backup(TaleUtils.getNewDataSource().getConnection());
            String sqlContent = backup.execute();

            File sqlFile = new File(bkAttachDir + sqlFileName);
            write(sqlContent, sqlFile, Charset.forName("UTF-8"));

            String zip = bkAttachDir + zipFile;
            ZipUtils.zipFile(sqlFile.getPath(), zip);

            if (!sqlFile.exists()) {
                throw new TipException("数据库备份失败");
            }
            sqlFile.delete();

            backResponse.setSqlPath(zipFile);

            // 10秒后删除备份文件
            *//*new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    new File(zip).delete();
                }
            }, 10 * 1000);*//*
        }
        return backResponse;
    }*/

    @Override
    public BackResponseBo backup(String bk_type, String bk_path, String fmt) throws Exception {
        BackResponseBo backResponse = new BackResponseBo();
        if (StringUtils.isBlank(bk_path)) {
            throw new TipException("请输入备份文件存储路径");
        }

        File bakPath = new File(bk_path);
        if (!bakPath.exists()) {
            bakPath.mkdirs();
        }

        String fPath = "";
        if ("attach".equals(bk_type)) {
            // 上传文件的路径
            //String bkAttachDir = AttachController.CLASSPATH + "upload";
            String bkAttachDir = Commons.getUploadBasePath();
            String fname = DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".zip";
            String attachPath = bk_path + "/" + "attachs_" + fname;
            ZipUtils.zipFolder(bkAttachDir, attachPath);

            fPath = new File("").getCanonicalPath() + File.separator + attachPath;
            backResponse.setAttachPath(fPath);
        }
        else if ("theme".equals(bk_type)) {
            // 上传文件的路径
            String bkThemesDir = FileUploadController.CLASSPATH + "templates/themes";

            String fname = DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".zip";
            String themesPath = bk_path + "/" + "themes_" + fname;
            ZipUtils.zipFolder(bkThemesDir, themesPath);

            fPath = new File("").getCanonicalPath() + File.separator + themesPath;
            backResponse.setThemePath(fPath);
        }
        if ("db".equals(bk_type)) {

            //String bkAttachDir = AttachController.CLASSPATH + "upload/";
            String bkAttachDir = bk_path + File.separator + "sql_bak/";
            if (!(new File(bkAttachDir)).isDirectory()) {
                File file = new File(bkAttachDir);
                if (!file.exists()) {
                    file.mkdirs();
                }
            }
            String sqlFileName = "tale_" + DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".sql";
            String zipFile = sqlFileName.replace(".sql", ".zip");

            Backup backup = new Backup(TaleUtils.getNewDataSource().getConnection());
            String sqlContent = backup.execute();

            File sqlFile = new File(bkAttachDir + sqlFileName);
            write(sqlContent, sqlFile, Charset.forName("UTF-8"));

            String zip = bkAttachDir + zipFile;
            ZipUtils.zipFile(sqlFile.getPath(), zip);

            if (!sqlFile.exists()) {
                throw new TipException("数据库备份失败");
            }

            fPath = new File("").getCanonicalPath() + File.separator + zip;
            sqlFile.delete();
            //backResponse.setSqlPath(zipFile);
            backResponse.setSqlPath(fPath);

            // 10秒后删除备份文件
            /*new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    new File(zip).delete();
                }
            }, 10 * 1000);*/
        }
        if ("md".equals(bk_type)) {
            String bkAttachDir_suffix = "md_bak/";
            if ("jblog".equals(appName)){
                bkAttachDir_suffix = "md_bak_jc/";
            }
            String bkAttachDir = bk_path + File.separator + bkAttachDir_suffix;
            File bkAttachDirFile = new File(bkAttachDir);
            if (!(bkAttachDirFile).isDirectory()) {
                File file = bkAttachDirFile;
                if (!file.exists()) {
                    file.mkdirs();
                    LOGGER.info("md文件备份目录{}创建成功！", bkAttachDir);
                }else{
                    LOGGER.info("md文件备份目录{}已经存在！", bkAttachDir);
                }
            }
            for (File file : bkAttachDirFile.listFiles()) {
                if (file.getName().contains(".zip")){
                    LOGGER.info("目录{}上已经存在压缩包，请删除后再打包！", file.getCanonicalPath());
                    backResponse.setMdPath(file.getCanonicalPath());
                    return backResponse;
                }
            }


            Set<Integer> articleIdSet = new HashSet<>();

            int p=0,limit=10;
            PageHelper.startPage(p, limit);
            List<ContentVo> data = contentDao.selectAll();
            while (!CollectionUtils.isEmpty(data)){
                LOGGER.info("backup md files,pageNum={},pageSize={},result={}", p,limit,data.size());
                for (ContentVo contentVo : data) {
                    if (articleIdSet.contains(contentVo.getCid())){
                        continue;
                    }

                    String mdContent = contentVo.getContent();
//                    ![2018-01-24_sso单点登录原理_5.png](http://runblog.com/blog/static/upload/127)
//                    ![垃圾回收总结-20180512213648411. png]({contextPath}/static/upload/272)
                    Map<String,String> map = getContentParamMap(mdContent);
                    if (!CollectionUtils.isEmpty(map)){
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            String upload = entry.getKey();
                            UploadVo uploadVo = uploadDao.selectByPrimaryKey(Long.valueOf(upload));
                            if (null != uploadVo){
                                String oldValue = entry.getValue();
                                if (StringUtils.isNotBlank(oldValue) && oldValue.contains("{contextPath}")){
                                    String oldSub = oldValue.substring(oldValue.indexOf("/"));
                                    mdContent = mdContent.replaceAll("\\{contextPath\\}" + oldSub,"../images/blogs/" + uploadVo.getFkey());
                                }else{
                                    mdContent = mdContent.replaceAll(oldValue,"../images/blogs/" + uploadVo.getFkey());
                                }
                            }else{
                                LOGGER.info("the article {} cannot find image by id:{},skip...", contentVo.getCid(),upload);
                            }
                        }
                    }
                    if (StringUtils.isNotBlank(mdContent)){
                        String title = contentVo.getTitle();
                        if (StringUtils.isNotBlank(title) && title.contains("/")){
                            title = title.replaceAll("\\/", "_");
                        }
                        File mdFile = new File(bkAttachDir + title + ".md");
                        write(mdContent, mdFile, Charset.forName("UTF-8"));
                    }
                    articleIdSet.add(contentVo.getCid());
                }

                p++;
                PageHelper.startPage(p, limit);
                data = contentDao.selectAll();
            }

            String zipFile_suffix = "md_jc_";
            if ("jblog".equals(appName)){
                zipFile_suffix = "md_jc_";
            }
            String zipFile = zipFile_suffix + DateKit.dateFormat(new Date(), fmt) + "_" + TaleUtils.getRandomNumber(5) + ".zip";
            String zip = bk_path + File.separator + zipFile;
            LOGGER.info("备份压缩包路径：{}", zip);
            ZipUtils.zipFolder(bkAttachDir, zip);
//            cn.hutool.core.util.ZipUtil.zip(bkAttachDirFile.getCanonicalPath(),zip);

            if (!new File(zip).exists()) {
                throw new TipException("md压缩包备份失败");
            }
            LOGGER.info("md文件压缩完成,共{}个文件",articleIdSet.size());

            fPath = new File("").getCanonicalPath() + File.separator + zip;
            backResponse.setMdPath(fPath);
        }
        return backResponse;
    }

    public static void main(String[] args) throws Exception{
        String imagePath1 = "![2018-01-24_sso单点登录原理_5.png](http://runblog.com/blog/static/upload/127)";
        String imagePath2 = "![垃圾回收总结-20180512213648411.png]({contextPath}/static/upload/272)";
//        List<String> targetStrings = getTargetStrings(imagePath);

//        String fkey1 = getFKey(imagePath1);
//        String fkey2 = getFKey(imagePath2);
//        System.out.println(fkey1);
//        System.out.println(fkey2);

        /*String content = "## 第一步\n" +
                "首先，服务器应用（下面简称“应用”）让用户通过Web表单将自己的用户名和密码发送到服务器的接口。这一过程一般是一个HTTP POST请求。**建议的方式是通过SSL加密的传输（https协议），从而避免敏感信息被嗅探。**\n" +
                "\n" +
                "![jwtauth1.png](http://runblog.com/blog/static/upload/164) \n" +
                "\n" +
                "## 第二步\n" +
                "接下来，应用和数据库核对用户名和密码。\n" +
                "\n" +
                "![jwtauth2.png]({contextPath}/static/upload/272)";
        Map<String, String> contentParamMap = getContentParamMap(content);

        for (Map.Entry<String, String> entry : contentParamMap.entrySet()) {
            String value = entry.getValue();
            if (value.contains("{contextPath}")){
                System.out.println(value);

                String sub = value.substring(value.indexOf("/"));
                System.out.println("sub=" + sub);
                value = value.replaceAll("\\{contextPath\\}" + sub,"111111");
                System.out.println(value);
            }
        }

        System.out.println(getContentParamMap(content));*/

//        String bkAttachDir = "D:\\install_packages\\myblog\\springboot安装包\\现在正在运行的版本\\20210410\\md_20210410";
//        String zip = "backup/md_202104101052_35423.zip";
//        ZipUtils.zipFolder(bkAttachDir, zip);

        String bkAttachDir = "D:\\install_packages\\myblog\\springboot安装包\\现在正在运行的版本\\20210410\\md_20210410";
        String bkAttachDir_new = "D:\\install_packages\\myblog\\springboot安装包\\现在正在运行的版本\\20210410\\md_20210410_new";
        copyFile(bkAttachDir, bkAttachDir_new);

//        String mdContent = "![jwtauth3.png](../images/blogs/2018/02/7pcfopmh9ci3gpc9jdg2jsmfgf.png)";
//        List<String> map = getTargetStrings(mdContent);
//        if (!CollectionUtils.isEmpty(map)){
//            for (String entry : map) {
//                String oldValue = entry;
//                if (StringUtils.isNotBlank(oldValue) && oldValue.contains("../images/blogs/")){
////                        String oldSub = oldValue.substring(oldValue.indexOf("/"));
//                    mdContent = mdContent.replaceAll("\\.\\./images/blogs/","../../images/blogs/");
//                }
//            }
//        }
//        System.out.println(mdContent);
    }

    private static void copyFile(String bkAttachDir, String bkAttachDir_new) throws Exception{
        File fileDir = new File(bkAttachDir);
        File[] listFiles = fileDir.listFiles();
        for (File file : listFiles) {
            if (file.isDirectory()){
                String tempPath = bkAttachDir_new + File.separator + file.getName();
                System.out.println(tempPath);
                copyFile(file.getCanonicalPath(),tempPath);
                continue;
            }
//            if (!file.getName().contains("八幅漫画理解")) {
//                continue;
//            }

            // 这种方式匹配不上，奇怪
//            String mdContent2 = FileUtil.readUtf8String(file);
//            List<String> map = getTargetStrings(mdContent2);
//            System.out.println("......");
//            System.out.println(map);

            List<String> paramList = new ArrayList<>();
            List<String> mdContent1 = FileUtil.readUtf8Lines(file);
            for (int i = 0; i < mdContent1.size(); i++) {
                List<String> map = getTargetStrings(mdContent1.get(i));
                paramList.addAll(map);
            }

            String mdContent = FileUtil.readUtf8String(file);
            if (!CollectionUtils.isEmpty(paramList)){
                mdContent = mdContent.replaceAll("\\.\\./images/blogs/","../../images/blogs/");
            }

            File mdFile = new File(bkAttachDir_new + File.separator + file.getName());
//            write(mdContent, mdFile, Charset.forName("UTF-8"));

            FileUtil.writeString(mdContent, mdFile, Charset.forName("UTF-8"));
        }
    }

    public static List<String> getTargetStrings(String str) {
        List<String> result = new ArrayList<String>();
        Pattern p = Pattern.compile("^!\\[(.+?)\\]\\((.+?)\\)");
        Matcher m = p.matcher(str);
        while (m.find()) {
//            result.add(m.group(1));
            String oldImagePath = m.group(2);
            if (oldImagePath!=null && oldImagePath.contains("../images/blogs/")){
                result.add(m.group(2));
            }
        }
        return result;
    }

    public static String getFKey(String str) {
        Pattern p = Pattern.compile("^!\\[(.+?)\\]\\((.+?)\\)");
        Matcher m = p.matcher(str);
        while (m.find()) {
            String oldImagePath = m.group(2);
            if (StringUtils.isBlank(oldImagePath)){
                return null;
            }
            if (oldImagePath.contains("/static/upload/")){
                String fkey = oldImagePath.substring(oldImagePath.lastIndexOf("/") + 1);

                return PatternKit.isNumber(fkey) ? fkey:"";
            }
        }
        return "";
    }

    public static Map<String,String> getContentParamMap(String str) {
        Map<String,String> map = new HashMap();
        if (StringUtils.isBlank(str)){
            return map;
        }
        Pattern p = Pattern.compile("!\\[(.+?)\\]\\((.+?)\\)");
        Matcher m = p.matcher(str);
        while (m.find()) {
            String oldImagePath = m.group(2);
            if (StringUtils.isNotBlank(oldImagePath) && oldImagePath.contains("/static/upload/")){
                String uploadId = oldImagePath.substring(oldImagePath.lastIndexOf("/") + 1);

                if (PatternKit.isNumber(uploadId)){
                    map.put(uploadId, m.group(2));
                }
            }
        }
        return map;
    }


    @Override
    public CommentVo getComment(Integer coid) {
        if (null != coid) {
            return commentDao.selectByPrimaryKey(coid);
        }
        return null;
    }

    @Override
    public StatisticsBo getStatistics() {
        return getStatistics(0);
    }

    @Override
    public StatisticsBo getStatistics(int uid) {
        LOGGER.debug("Enter getStatistics method");
        StatisticsBo statistics = new StatisticsBo();

        /*ContentVoExample contentVoExample = new ContentVoExample();
        contentVoExample.createCriteria().andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());
        Long articles =   contentDao.countByExample(contentVoExample);

        Long comments = commentDao.countByExample(new CommentVoExample());

        Long attachs = attachDao.countByExample(new AttachVoExample());

        MetaVoExample metaVoExample = new MetaVoExample();
        metaVoExample.createCriteria().andTypeEqualTo(Types.LINK.getType());
        Long links = metaDao.countByExample(metaVoExample);*/
        //content
        Example contentExample = new Example(ContentVo.class);
        Example.Criteria content = contentExample.createCriteria();
        //comment
        Example commentExample = new Example(CommentVo.class);
        Example.Criteria comment = commentExample.createCriteria();
        //upload
        Example uploadExample = new Example(UploadVo.class);
        Example.Criteria upload = uploadExample.createCriteria();
        //meta
        Example metaExample = new Example(MetaVo.class);
        Example.Criteria meta = metaExample.createCriteria();
        if(uid!=0){
            content.andEqualTo("authorId",uid);
            comment.andEqualTo("ownerId",uid);
            upload.andEqualTo("authorId",uid);
            meta.andEqualTo("ownerId",uid);
        }
        content.andEqualTo("type",Types.ARTICLE.getType()).andEqualTo("status",Types.PUBLISH.getType());
        int articles = contentDao.selectCountByExample(contentExample);
        int comments = commentDao.selectCountByExample(commentExample);

        upload.andEqualTo("delFlag",false);
        int attachs = uploadDao.selectCountByExample(uploadExample);

        meta.andEqualTo("type",Types.LINK.getType());
        int links = metaDao.selectCountByExample(metaExample);

        statistics.setArticles(Long.parseLong(articles+""));
        statistics.setComments(Long.parseLong(comments+""));
        statistics.setAttachs(Long.parseLong(attachs+""));
        statistics.setLinks(Long.parseLong(links+""));
        LOGGER.debug("Exit getStatistics method");
        return statistics;
    }

    @Override
    public List<ArchiveBo> getArchives() {
        LOGGER.debug("Enter getArchives method");
        List<ArchiveBo> archives = contentDao.findReturnArchiveBo();
        if (null != archives) {
            archives.forEach(archive -> {
                /*ContentVoExample example = new ContentVoExample();
                ContentVoExample.Criteria criteria = example.createCriteria().andTypeEqualTo(Types.ARTICLE.getType()).andStatusEqualTo(Types.PUBLISH.getType());*/
                Example example = new Example(ContentVo.class);
                Example.Criteria criteria = example.createCriteria().andEqualTo("type",Types.ARTICLE.getType()).andEqualTo("status",Types.PUBLISH.getType());

                example.setOrderByClause("created desc");
                String date = archive.getDate();
                Date sd = DateKit.dateFormat(date, "yyyy年MM月");
                int start = DateKit.getUnixTimeByDate(sd);
                int end = DateKit.getUnixTimeByDate(DateKit.dateAdd(DateKit.INTERVAL_MONTH, sd, 1)) - 1;
                /*criteria.andCreatedGreaterThan(start);
                criteria.andCreatedLessThan(end);*/
                criteria.andBetween("created",start,end);
                List<ContentVo> contentss = contentDao.selectByExample(example);
                archive.setArticles(contentss);
            });
        }
        LOGGER.debug("Exit getArchives method");
        return archives;
    }

    @Override
    public List<MetaDto> metas(String type, String orderBy, int limit){
        LOGGER.debug("Enter metas method:type={},order={},limit={}", type, orderBy, limit);
        List<MetaDto> retList=null;
        if (StringUtils.isNotBlank(type)) {
            if(StringUtils.isBlank(orderBy)){
                orderBy = "count desc, a.mid desc";
            }
            if(limit < 1 || limit > WebConst.MAX_POSTS){
                limit = 10;
            }
            Map<String, Object> paraMap = new HashMap<>();
            paraMap.put("type", type);
            paraMap.put("order", orderBy);
            paraMap.put("limit", limit);
            retList= metaDao.selectFromSql(paraMap);
        }
        LOGGER.debug("Exit metas method");
        return retList;
    }


    private void write(String data, File file, Charset charset) {
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(file);
            os.write(data.getBytes(charset));
        } catch (IOException var8) {
            throw new IllegalStateException(var8);
        } finally {
            if(null != os) {
                try {
                    os.close();
                } catch (IOException var2) {
                    var2.printStackTrace();
                }
            }
        }

    }

}
